package com.css.casualties.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.css.attachment.entity.AttachmentInfoEntity;
import com.css.attachment.entity.ParsingResult;
import com.css.attachment.service.AttachmentService;
import com.css.attachment.vo.AttachVO;
import com.css.constants.StructureConstants;
import com.css.casualties.entity.*;
import com.css.casualties.mapper.RyswModelMapper;
import com.css.casualties.service.*;
import com.css.config.ThreadPoolConfig;
import com.css.enums.GeomTypeEnum;
import com.css.util.MapToEntityUtil;
import com.css.util.ShpAnalyticUtil;
import com.css.util.ZipUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.postgis.Geometry;
import org.postgis.PGgeometry;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author Dbj
 * @date 2024年09月19日  9:39
 */
@Service
public class RyswModelServiceImpl extends ServiceImpl<RyswModelMapper, RyswModel> implements RyswModelService {

    @Resource
    RyswModelService1 ryswModelService1;
    @Resource
    RyswModelService2 ryswModelService2;
    @Resource
    RyswModelService3 ryswModelService3;
    @Resource
    RyswModelService4 ryswModelService4;
    @Resource
    RyswModelService5 ryswModelService5;
    @Resource
    RyswModel3DataService ryswModel3DataService;
    @Resource
    RyswModel4DataService ryswModel4DataService;

    @Resource
    AttachmentService attachmentService;

    @Override
    @DSTransactional
    public void updateRyswModelData(Map<String, Object> map,String divisionCode,String userId) throws Exception {
        Date updateTime = DateUtil.date();
        ThreadPoolExecutor executor = ThreadPoolConfig.exeBizThreadPoolExecutor;
        //时间因子和报告使用模型可以不维护，当不维护时不需要传递key为model的对象数据
        if(ObjectUtils.isNotEmpty(map.get("model"))){
            RyswModel model = MapToEntityUtil.mapToEntity((LinkedHashMap) map.get("model"), RyswModel.class);
            //保存5个模型公用的时间因子和报告使用的模型
            this.baseMapper.delete(null);
            this.baseMapper.insert(model);
        }
        //保存模型1数据
        LinkedHashMap lhm1 = (LinkedHashMap) map.get("model1");
        RyswModel1 ryswModel1 = MapToEntityUtil.mapToEntity(lhm1, RyswModel1.class);
        ryswModel1.setUpdateTime(updateTime);
        ryswModel1.setUpdateUserId(userId);
        ryswModelService1.updateById(ryswModel1);
        //保存模型2数据
        RyswModel2 ryswModel2 = MapToEntityUtil.mapToEntity((LinkedHashMap) map.get("model2"), RyswModel2.class);
        ryswModel2.setUpdateTime(updateTime);
        ryswModel2.setUpdateUserId(userId);
        ryswModelService2.updateById(ryswModel2);
        String finalUserId = userId;
        //保存模型3数据  涉及到文件的解压和数据的解析与保存用异步县城是的方式执行
        CompletableFuture completableFuture3 = CompletableFuture.supplyAsync(()->{
            RyswModel3 ryswModel3 = null;
            try {
                DynamicDataSourceContextHolder.push("province_"+divisionCode);
                ryswModel3 = MapToEntityUtil.mapToEntity((LinkedHashMap) map.get("model3"), RyswModel3.class);
                ryswModel3.setUpdateTime(updateTime);
                ryswModel3.setUpdateUserId(finalUserId);
                saveDeadInjurePercent(ryswModel3);
                ryswModelService3.updateById(ryswModel3);

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                DynamicDataSourceContextHolder.clear();
            }
            return 1;
        },executor);
        //保存模型4数据  涉及到文件的解压和数据的解析与保存用异步县城是的方式执行
        CompletableFuture completableFuture4 = CompletableFuture.supplyAsync(()->{
            RyswModel4 ryswModel4 = null;
            try {
                DynamicDataSourceContextHolder.push("province_"+divisionCode);
                ryswModel4 = MapToEntityUtil.mapToEntity((LinkedHashMap) map.get("model4"), RyswModel4.class);
                //ryswModel4.setAttachId("cf5b0f76122b4ab489cd4bcfb2734048,d9c8dd0d2cfd4dad8604be8e1b64a388,6d7eb668fa334d6d88f73ae5fc1212b5,8c30dbfa6f874638add9f9268ed56373");
                ryswModel4.setUpdateTime(updateTime);
                ryswModel4.setUpdateUserId(finalUserId);
                saveYsxData(ryswModel4);
                ryswModelService4.updateById(ryswModel4);

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                DynamicDataSourceContextHolder.clear();
            }
            return 1;
        },executor);
        //保存模型5数据
        List<LinkedHashMap> lhm5 = (List<LinkedHashMap>) map.get("model5");
        if(ObjectUtils.isNotEmpty(lhm5) && lhm5.size()>0){
            for (LinkedHashMap linkedHashMap : lhm5) {
                RyswModel5 ryswModel5 = MapToEntityUtil.mapToEntity(linkedHashMap, RyswModel5.class);
                ryswModel5.setUpdateUserId(userId);
                ryswModel5.setUpdateTime(updateTime);
                ryswModelService5.updateById(ryswModel5);
            }
        }

        //当最慢的两个异步线程执行完毕后保存逻辑才完成
        CompletableFuture.allOf(completableFuture4, completableFuture3).get();
    }

    private void deleteData(int i){
        //先删除指定类型的数据
        LambdaQueryWrapper<RyswModel4Data> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(RyswModel4Data::getStructureType,(i+1)+"");
        ryswModel4DataService.remove(lambdaQueryWrapper);
    }
    /**
     * 保存模型4易损性数据
     */
    private void saveYsxData( RyswModel4 ryswModel4) throws Exception {
        RyswModel4 one = ryswModelService4.getOne(null);
        List<String> strings = new ArrayList<>();
        if(ObjectUtils.isNotEmpty(one) && StringUtils.isNotBlank(one.getAttachId())){
            strings = Arrays.asList(one.getAttachId().split(","));
        }
        //先删除之前关联的易损性数据如果存在的话
        //ryswModel4DataService.remove(null);
        //根据附件id获取上传的zip压缩包的全路径
        String attachIds = ryswModel4.getAttachId();
        String[] split = attachIds.split(",");
        for(int i=0;i<4;i++){
            String s1 = strings.get(i);
            String attachId = split[i];
            if(s1.equals(attachId)){
                continue;
            }
            deleteData(i);
            //解析指定类型的数据
            AttachmentInfoEntity attach = attachmentService.getAttach(attachId);
            String fileName = attach.getFileDirId()+attach.getAttachPath();
            //解压zip
            ArrayList<MultipartFile> multipartFiles = ZipUtils.decompress(fileName);
            String prj = "";
            String shp = "";
            String cpg = "";
            String root = StrUtil.removeSuffix(fileName,".zip");
            for (MultipartFile multipartFile : multipartFiles) {
                String originalFilename = multipartFile.getOriginalFilename();
                assert originalFilename != null;
                if(originalFilename.contains(".prj")){
                    prj = root + File.separator + originalFilename;
                }
                if(originalFilename.contains(".shp") && !originalFilename.contains(".shp.xml")){
                    shp = root + File.separator + originalFilename;
                }
                if (originalFilename.contains(".cpg")) {
                    cpg = root + File.separator +  originalFilename;
                }
            }
            if("".equals(cpg)){
                cpg = "GBK";
            }
            //解析shp获取数据
            ParsingResult shpData = ShpAnalyticUtil.analytic(prj, cpg, shp);
            if (!shpData.getMessage().equals("ok")) {
                throw new RuntimeException(shpData.getMessage());
            }
            //根据固定的文件名称判断上传的文件是哪种建筑物结构（多层、单层、其他、钢混）
            String structureType = shp.contains("ganghun")?"ganghun":shp.contains("danceng")?"danceng":shp.contains("qita")?"qita":"qiti";
            //组建保存烈度与易损性数据
            getYsxList(shpData.getShpList(), ryswModel4.getId(), structureType);
            //删除解压的文件
            ZipUtils.delFolder(root);
        }
    }
    /**
     * 组建保存烈度与伤亡率数据
     */
    private void getModel3DataList(List<List<Object>> shpList, String modelId) throws Exception {
        List<RyswModel3Data> arrayList = new ArrayList<>(shpList.size());

        //不同烈度的数据放在了同一行，保存时一个烈度的数据一行数据
        for (int i = 0; i < shpList.size(); i++) {
            List<Object> list = shpList.get(i);
            //获取上传文件的坐标系 用于保存数据时的坐标系
//            String code = getZbx(list);
            String code = "4490";
            PGgeometry pGgeometry = new PGgeometry(ShpAnalyticUtil.checkGeomTypeOf(list.get(0).toString(), GeomTypeEnum.MULTIPOLYGON));
            Geometry geometry = pGgeometry.getGeometry();
            geometry.setSrid(Integer.parseInt(code));
            int index = 2;
            for(int j=0;j<7;j++){
                if(index==list.size()){
                    break;
                }
                RyswModel3Data model3Data = RyswModel3Data.builder().id(StrUtil.uuid().replace("-",""))
                        .modelId(modelId).intensity(ShpAnalyticUtil.getStringVal(list.get(index++), "烈度", true))
                        .deadRatio(new BigDecimal(ShpAnalyticUtil.getStringVal(list.get(index++), "死亡率", true)))
                        .injureRatio(new BigDecimal(ShpAnalyticUtil.getStringVal(list.get(index++), "受伤率", true)))
                        .code(code)
                        .geom(geometry.toString())
                        .build();
                arrayList.add(model3Data);
            }
        }
        if(arrayList.size()>0){
            ryswModel3DataService.saveBatch(arrayList);
        }
    }

    /**
     * 获取坐标系
     */
    private String getZbx(List<Object> shpList){
        int size = shpList.size();
        String code = "4490";
        if(ObjectUtils.isNotEmpty(shpList.get(size-1))){
            code = (String) shpList.get(size-1);
        }
        return code;
    }
    /**
     * 组建保存烈度与易损性数据
     */
    private void getYsxList(List<List<Object>> shpList,String modelId,String structureType) throws Exception {
        List<RyswModel4Data> arrayList = new ArrayList<>();
        structureType = StructureConstants.getCodeByType(structureType);

        for (int i = 0; i < shpList.size(); i++) {
            List<Object> list = shpList.get(i);
            //获取上传文件的坐标系 用于保存数据时的坐标系
            String code = "4490";
            PGgeometry pGgeometry = new PGgeometry(ShpAnalyticUtil.checkGeomTypeOf(list.get(0).toString(), GeomTypeEnum.MULTIPOLYGON));
            Geometry geometry = pGgeometry.getGeometry();
            geometry.setSrid(Integer.parseInt(code));
            int index = 8;
            //不同烈度的数据放在了同一行，保存时一个烈度的数据一行数据
            String pid = UUID.randomUUID().toString().replace("-","");
            for(int j=0;j<5;j++){
                if(index==list.size()){
                    break;
                }
                try{
                    Object Intensity = list.get(index);
                    if(ObjectUtils.isEmpty(Intensity)){
                        break;
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }

                try{
                    RyswModel4Data obj = RyswModel4Data
                            .builder()
                            .id(StrUtil.uuid().replace("-",""))
                            .modelId(modelId)
                            .geom(geometry.toString())
                            .code(code)
                            .structureType(structureType)
                            .intensity(ShpAnalyticUtil.getStringVal(list.get(index++), "烈度", true))
                            .basic(new BigDecimal(ShpAnalyticUtil.getStringVal(list.get(index++), "基本完好", true)))
                            .slight(new BigDecimal(ShpAnalyticUtil.getStringVal(list.get(index++), "轻微破坏", true)))
                            .mid(new BigDecimal(ShpAnalyticUtil.getStringVal(list.get(index++), "中等破坏", true)))
                            .serious(new BigDecimal(ShpAnalyticUtil.getStringVal(list.get(index++), "严重破坏", true)))
                            .destory(new BigDecimal(ShpAnalyticUtil.getStringVal(list.get(index++), "毁坏", true)))
                            .pid(pid)
                            .build();
                    arrayList.add(obj);
                }catch (Exception e){
                    e.printStackTrace();
                }


            }
        }
        if(arrayList.size()>0){
            ryswModel4DataService.saveBatch(arrayList);
        }
    }

    /**
     * 保存模型3 烈度和死亡重伤占比  小数值
     */
    private void saveDeadInjurePercent(RyswModel3 ryswModel3) throws Exception {
        String attachId = ryswModel3.getAttachId();
        //先查询模型三的id
        RyswModel3 one = ryswModelService3.getOne(null);
        if(one.getAttachId().equals(attachId)){
            //如果完全相同的不再操作
            return;
        }
        //删除之前保存的所有的伤亡率与烈度的关联数据如果存在的话
        ryswModel3DataService.remove(null);

        AttachmentInfoEntity attach = attachmentService.getAttach(attachId);
        String fileName = attach.getFileDirId()+attach.getAttachPath();
        ArrayList<MultipartFile> multipartFiles = ZipUtils.decompress(fileName);
        String prj = "";
        String shp = "";
        String root = StrUtil.removeSuffix(fileName,".zip");
        for (MultipartFile multipartFile : multipartFiles) {
            String originalFilename = multipartFile.getOriginalFilename();
            assert originalFilename != null;
            if(originalFilename.contains(".prj")){
                prj = root + File.separator + originalFilename;
            }
            if(originalFilename.contains(".shp") && !originalFilename.contains(".shp.xml")){
                shp = root + File.separator +  originalFilename;
            }
        }
        ParsingResult shpData = ShpAnalyticUtil.analytic(prj, "UTF-8", shp);
        if (!shpData.getMessage().equals("ok")) {
            throw new RuntimeException(shpData.getMessage());
        }
        //组建保存烈度与伤亡率数据
        getModel3DataList(shpData.getShpList(), ryswModel3.getId());
        //删除解压的文件
        ZipUtils.delFolder(root);
    }
    @Override
    public Map<String, Object> details() {
        Map<String, Object> map = new HashMap<>();
        //获取公共模型信息
        RyswModel model = getOne(null);
        map.put("model",model);
        //获取模型1信息
        RyswModel1 ryswModel1 = ryswModelService1.getBaseMapper().selectOne(null);
        ryswModel1.setUpdateTime(null);
        map.put("model1",ryswModel1);
        //获取模型2信息
        RyswModel2 ryswModel2 = ryswModelService2.getBaseMapper().selectOne(null);
        ryswModel2.setUpdateTime(null);
        map.put("model2",ryswModel2);
        //获取模型3信息
        RyswModel3 ryswModel3 = ryswModelService3.getBaseMapper().selectOne(null);
        if(StringUtils.isNotBlank(ryswModel3.getAttachId())){
            ryswModel3.setList(getAttach(ryswModel3.getAttachId()));
        }else{
            ryswModel3.setList(new ArrayList<>());
        }
        ryswModel3.setUpdateTime(null);
        map.put("model3",ryswModel3);
        //获取模型4信息
        RyswModel4 ryswModel4 = ryswModelService4.getBaseMapper().selectOne(null);
        if(StringUtils.isNotBlank(ryswModel4.getAttachId())){
            ryswModel4.setList(getAttach(ryswModel4.getAttachId()));
        }else{
            ryswModel4.setList(new ArrayList<>());
        }
        ryswModel4.setUpdateTime(null);
        map.put("model4",ryswModel4);
        //获取模型5信息
        List<RyswModel5> ryswModel5 = ryswModelService5.getBaseMapper().selectList(null);
        ryswModel5.forEach(entity->{
            entity.setUpdateTime(null);
        });
        map.put("model5",ryswModel5);
        return map;
    }

    private List<AttachVO> getAttach(String attachId){
        String[] idSplit = attachId.split(",");
        List<AttachVO> list = new ArrayList<>();
        for (String aid : idSplit) {
            AttachmentInfoEntity attach = attachmentService.getAttach(aid);
            if(attach!=null){
                AttachVO attachVO = new AttachVO();
                attachVO.setAttachId(aid);
                attachVO.setAttachName(attach.getAttachName());
                list.add(attachVO);
            }

        }
        return list;
    }
}


